package org.sunyaxing.transflow.transflowapp.plugins.inputs;

import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.thread.ThreadUtil;
import com.alibaba.fastjson2.JSONObject;
import org.gitlab4j.api.GitLabApi;
import org.gitlab4j.api.GitLabApiException;
import org.gitlab4j.api.models.Diff;
import org.gitlab4j.api.models.DiffRef;
import org.gitlab4j.api.models.MergeRequest;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.sunyaxing.transflow.api.HandleData;
import org.sunyaxing.transflow.api.TransData;
import org.sunyaxing.transflow.api.common.Handle;
import org.sunyaxing.transflow.api.common.ano.Extension;
import org.sunyaxing.transflow.api.common.ano.HandleItem;
import org.sunyaxing.transflow.api.common.ano.JobParamItem;
import org.sunyaxing.transflow.api.common.ano.ScopeContentCheck;
import org.sunyaxing.transflow.api.extensions.base.ExtensionContext;
import org.sunyaxing.transflow.api.extensions.base.types.TransFlowInput;
import org.sunyaxing.transflow.transflowapp.controllers.JobLogsWs;
import org.sunyaxing.transflow.transflowapp.plugins.inputs.gitlabairevire.GitlabHook;
import org.sunyaxing.transflow.transflowapp.plugins.outs.GitlabAiReviewCodeOutputExt;

import java.util.List;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.function.Function;

@Extension("gitlab-hook-input")
@ScopeContentCheck(value = {
        @JobParamItem(field = "name", label = "name", defaultValue = "DEF"),
        @JobParamItem(field = "gitHost", label = "gitHost", defaultValue = "http://127.0.0.1"),
        @JobParamItem(field = "gitToken", label = "gitToken", type = "password", defaultValue = "gitToken"),
        @JobParamItem(field = "states", label = "states", defaultValue = "preparing"),
}, handle = @HandleItem(field = "data", label = "接口数据"))
public class GitLabHookInputExt extends TransFlowInput<String, String> {
    private static final Logger log = LoggerFactory.getLogger(GitLabHookInputExt.class);

    private String name;
    private String states;
    private GitLabApi gitLabApi;

    public GitLabHookInputExt(ExtensionContext extensionContext) {
        super(extensionContext);
    }

    @Override
    public void commit(HandleData<String> handleData) {

    }

    @Override
    protected void afterInitHandler(JSONObject config, List<Handle> handles) {
        name = config.getString("name");
        // 将当前对象注册到 hook
        states = config.getString("states");
        String gitHost = config.getString("gitHost");
        String gitToken = config.getString("gitToken");
        log.info("【GitLabApi】 {} {}", gitHost, gitToken);
        this.gitLabApi = new GitLabApi(gitHost, gitToken);
        this.gitLabApi.setIgnoreCertificateErrors(true);
        this.gitLabApi.enableRequestResponseLogging();
        GitlabHook.registerHook(name, this);
    }

    @Override
    public Function<TransData<String>, String> parseHandleToConsumer(String handleId, String handleValue) {
        return TransData::getData;
    }

    public void putData(JSONObject mergeRequestEvent) {
        JobLogsWs.sendMessage(jobId, "【{}】接收到推送数据 {}", extensionContext.getNodeName(), mergeRequestEvent);
        JSONObject project = mergeRequestEvent.getJSONObject("project");
        JSONObject objectAttr = mergeRequestEvent.getJSONObject("object_attributes");
        String state = objectAttr.getString("merge_status");
        // 获取 projectId 和 mergeRequestIid
        Long projectId = project.getLong("id");
        Long mergeRequestIid = objectAttr.getLong("iid");
        if (states.contains(state)) {
            try {
                getDiffFromGitlab(projectId, mergeRequestIid, new AtomicInteger(0));
            } catch (GitLabApiException e) {
                log.info("【P[{}] R[{}]】 GITLAB API 调用失败", projectId, mergeRequestIid);
            }
        } else {
            log.info("【P[{}] R[{}]】 状态为 {} 不需要处理", projectId, mergeRequestIid, state);
        }
    }

    public boolean getDiffFromGitlab(Long projectId, Long mergeRequestIid, AtomicInteger retry) throws GitLabApiException {
        int sleepTime = retry.get() * 2;
        log.info("【P[{}] R[{}]】 准备{}s后检查", projectId, mergeRequestIid, sleepTime);
        ThreadUtil.sleep(sleepTime, TimeUnit.SECONDS);
        log.info("【P[{}] R[{}]】 获取差异中...", projectId, mergeRequestIid);
        MergeRequest mergeRequestDiffs = this.gitLabApi.getMergeRequestApi().getMergeRequestChanges(projectId, mergeRequestIid);
        if (CollectionUtil.isEmpty(mergeRequestDiffs.getChanges())) {
            log.info("【P[{}] R[{}]】 未获取到差异部分，准备重试第 {} 次", projectId, mergeRequestIid, retry.incrementAndGet());
            if (retry.get() > 3) {
                log.info("【P[{}] R[{}]】 尝试3次失败，跳过", projectId, mergeRequestIid);
                return false;
            } else {
                return getDiffFromGitlab(projectId, mergeRequestIid, retry);
            }
        } else {
            log.info("【P[{}] R[{}]】 成功获取差异 {}", projectId, mergeRequestIid, JSONObject.toJSONString(mergeRequestDiffs));
            // 准备必要原始数据
            GitlabAiReviewCodeOutputExt.GitLabNoteMsg orgData = new GitlabAiReviewCodeOutputExt.GitLabNoteMsg();
            orgData.setMrId(mergeRequestIid);
            orgData.setPid(projectId);
            for (Diff diff : mergeRequestDiffs.getChanges()) {
                String diffStr = diff.getDiff();
                orgData.setUserMsg(diffStr);
                this.handlerMap.forEach((handleId, handler) -> {
                    TransData<String> transData = new TransData<>(null, JSONObject.toJSONString(orgData));
                    this.handle(handleId, transData);
                });
            }
            return true;
        }
    }

    @Override
    public void destroy() {
        GitlabHook.unregisterHook(name);
        gitLabApi.close();
    }
}
